# coding=utf-8
import os
from string import Template

from typing import List
from utils.templete_file import TempleteFile
from parse.parse_types import Module
from utils.util import writeFile


templete_h = Template(
    """
#pragma once

#include <vector>
#include <transfer/common.h>
#include "common.h"

namespace ecarx::transfer::utils{
    
    std::vector<Signal> ipcpToPASignals(const ipcp::IpcpValue&);

}
"""
)
templete_cpp = Template(
    """
#include "convertIPCPValueToPA.h"
#include <arpa/inet.h>
#include <string.h>
#include "vp/SignalServiceId.h"
#include "ap/signal_id.h"

${ecu_h}

namespace ecarx::transfer::utils{

    
    std::vector<Signal> ipcpToPASignals(const ipcp::IpcpValue& ipValue){

        std::vector<Signal> signals={};
        switch(ipValue.service_id){
            ${codes}
        }
        return signals;
    }
}
"""
)
case_tempelet = Template(
"""
            case ${id}:{
                ${codes}
            }
            break;
""")


class Case(TempleteFile):

    def __init__(self, id) -> None:
        self.id = id
        self.code = []

    def codeBlock(self, code: str):
        self.code = code
        return self

    def toStr(self) -> str:
        return case_tempelet.substitute(id=self.id, codes=self.code)


def generateIPCP2PA(modules: List[Module],outDir:str):
    module_headers = []
    cases = []
    for module in modules:
        if len(module.pa_apis) > 0:
            module_headers.append("#include \"vp/{0}.h\"".format(module.name))
            case = Case(
                "EcarXSignalServiceID::serviceID_{0}".format(module.name.upper()))
            templete = Template("""
                constexpr int structLen=(int)sizeof(Op${module}_Notification);
                int bufferLength=structLen;
                if (ipValue.payload_len < structLen  ) {
                    bufferLength = ipValue.payload_len;
                }
                signals.reserve(${len});
                Op${module}_Notification data;
                memcpy(&data, ipValue.payload, bufferLength);
                ${signals}""")
            normal_pa_templete = Template("""
                Signal ${signal_name};
                ${signal_name}.signalId = ${signalId};
                ${signal_name}.value.bytes.resize(sizeof(${PA_Type}),0);
                ${PA_Type} ${var};
                //成员赋值
                ${members}
                memcpy(${signal_name}.value.bytes.data(),&${var}, sizeof(${PA_Type}));
                signals.emplace_back(${signal_name});""")
            # 逐个解析所有的信号
            parse = []
            for pa in module.pa_apis:
                members = []
                payload = pa.getPayload()
                #PA只有结构体
                if payload.isStructPayload():
                    structPayload = payload.getStructPayload()
                    if structPayload.name == 'pamculogpanic':
                        continue
                    for paMember in structPayload.members:
                        if paMember.is_array:
                            members.append(
                                "for(size_t i = 0;i<{len};++i){{".format(len=str(paMember.array_len)))
                            members.append("    {var}.{property}[i] = {value};".format(
                                var=structPayload.name,
                                property = paMember.member_name,
                                value=paMember.fromNet("data.{var}.{property}[i]".format(
                                    var=structPayload.name,
                                    property=paMember.member_name,
                                ))
                            ))
                            members.append("}")
                        else:
                            members.append("{var}.{property} = {value};".format(
                                var=structPayload.name,
                                property=paMember.member_name,
                                value =paMember.fromNet("data.{var}.{property}".format(
                                    var=structPayload.name,
                                    property=paMember.member_name,
                                ))
                            ))
                    parse.append(normal_pa_templete.substitute(
                        signal_name="{0}Signal".format(structPayload.name),
                        signalId="(int32_t)PASignal::"+pa.api,
                        PA_Type=structPayload.type_name,
                        var=structPayload.name,
                        members="\n\t".expandtabs(16).join(members)
                    ))
            case.codeBlock(templete.substitute(
                module=module.name, len=len(module.pa_apis), signals="".join(parse)))
            cases.append(case)

    writeFile(os.path.join(outDir,"src"), "convertIPCPValueToPA.h",
              lambda file: file.write(templete_h.substitute()))
    writeFile(os.path.join(outDir,"src"), "convertIPCPValueToPA.cpp", lambda file:
              file.write(templete_cpp.substitute(ecu_h="\n".join(module_headers), codes="".join([case.toStr() for case in cases]))))
